// -------------------------------------------------------------------------------------------------
// Copyright 2024 Kearn Chen, kearn.chen@aliyun.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// -------------------------------------------------------------------------------------------------
// Description :
//             1. Instruction Decoder Unit
// -------------------------------------------------------------------------------------------------

module k0a_core_idu (
    input  wire         bmu2idu_valid     ,
    input  wire [31:0]  bmu2idu_instr     ,
    input  wire [17:0]  bmu2idu_pc_cur    ,
    input  wire [17:0]  bmu2idu_pc_nxt    ,

    output wire         idu2bmu_pc_set    ,
    output wire [17:0]  idu2bmu_pc_new    ,
    input  wire         bmu2idu_pc_ack    ,

    output wire         idu2bmu_ls_req    ,
    output wire         idu2bmu_ls_cmd    ,
    output wire [1:0]   idu2bmu_ls_size   ,
    output wire [19:0]  idu2bmu_ls_addr   ,
    output wire [31:0]  idu2bmu_ls_wdata  ,
    input  wire [31:0]  bmu2idu_ls_rdata  ,

    output wire         idu2gpr_we        ,
    output wire [3:0]   idu2gpr_waddr     ,
    output wire [31:0]  idu2gpr_wdata     ,
    output wire [3:0]   idu2gpr_raddr1    ,
    output wire [3:0]   idu2gpr_raddr2    ,
    input  wire [31:0]  gpr2idu_rdata1    ,
    input  wire [31:0]  gpr2idu_rdata2    ,

    output wire         idu2csr_we        ,
    output wire [11:0]  idu2csr_addr      ,
    output wire [31:0]  idu2csr_wdata     ,
    input  wire [31:0]  csr2idu_rdata     ,

    input  wire [17:0]  csr2idu_mepc      ,
    input  wire [17:0]  csr2idu_mtvec     ,
    output wire         idu2csr_mepc_set  ,
    output wire [17:0]  idu2csr_mepc_nxt  ,

    output wire [3:0]   idu2alu_op        ,
    output wire [31:0]  idu2alu_rs1       ,
    output wire [31:0]  idu2alu_rs2       ,
    input  wire [31:0]  alu2idu_res       ,
    input  wire         alu2idu_cmp       ,
    output wire [19:0]  idu2alu_addr1     ,
    output wire [19:0]  idu2alu_addr2     ,
    input  wire [19:0]  alu2idu_addro     ,

    input  wire         cic2idu_int_req   ,
    output wire         idu2cic_int_ack   ,
    output wire         idu2cic_int_mret
);

wire rvi_imm = bmu2idu_valid & bmu2idu_instr[6:0] == 7'b0010011;
wire rvi_reg = bmu2idu_valid & bmu2idu_instr[6:0] == 7'b0110011;
wire rvi_ldr = bmu2idu_valid & bmu2idu_instr[6:0] == 7'b0000011;
wire rvi_str = bmu2idu_valid & bmu2idu_instr[6:0] == 7'b0100011;
wire rvi_bra = bmu2idu_valid & bmu2idu_instr[6:0] == 7'b1100011;
wire rvi_jal = bmu2idu_valid & bmu2idu_instr[6:0] == 7'b1101111;
wire rvi_jar = bmu2idu_valid & bmu2idu_instr[6:0] == 7'b1100111;
wire rvi_sys = bmu2idu_valid & bmu2idu_instr[6:0] == 7'b1110011;
wire rvi_lui = bmu2idu_valid & bmu2idu_instr[6:0] == 7'b0110111;
wire rvi_aui = bmu2idu_valid & bmu2idu_instr[6:0] == 7'b0010111;

wire rvi_func3_000 = bmu2idu_instr[14:12] == 3'b000;
wire rvi_func3_001 = bmu2idu_instr[14:12] == 3'b001;
wire rvi_func3_010 = bmu2idu_instr[14:12] == 3'b010;
wire rvi_func3_011 = bmu2idu_instr[14:12] == 3'b011;
wire rvi_func3_100 = bmu2idu_instr[14:12] == 3'b100;
wire rvi_func3_101 = bmu2idu_instr[14:12] == 3'b101;
wire rvi_func3_110 = bmu2idu_instr[14:12] == 3'b110;
wire rvi_func3_111 = bmu2idu_instr[14:12] == 3'b111;

wire rvi_func7_zero = bmu2idu_instr[31:25] == 7'h00;

wire instr_lui    = rvi_lui;
wire instr_auipc  = rvi_aui;
wire instr_jal    = rvi_jal;
wire instr_jalr   = rvi_jar & rvi_func3_000;
wire instr_beq    = rvi_bra & rvi_func3_000;
wire instr_bne    = rvi_bra & rvi_func3_001;
wire instr_blt    = rvi_bra & rvi_func3_100;
wire instr_bge    = rvi_bra & rvi_func3_101;
wire instr_bltu   = rvi_bra & rvi_func3_110;
wire instr_bgeu   = rvi_bra & rvi_func3_111;
wire instr_lb     = rvi_ldr & bmu2idu_instr[13:12] == 2'b00;
wire instr_lh     = rvi_ldr & bmu2idu_instr[13:12] == 2'b01;
wire instr_lw     = rvi_ldr & rvi_func3_010;
wire instr_lbu    = rvi_ldr & bmu2idu_instr[14];
wire instr_lhu    = rvi_ldr & bmu2idu_instr[14];
wire instr_sb     = rvi_str & rvi_func3_000;
wire instr_sh     = rvi_str & rvi_func3_001;
wire instr_sw     = rvi_str & rvi_func3_010;
wire instr_addi   = rvi_imm & rvi_func3_000;
wire instr_slti   = rvi_imm & rvi_func3_010;
wire instr_sltiu  = rvi_imm & rvi_func3_011;
wire instr_slt    = rvi_reg & rvi_func3_010 & rvi_func7_zero;
wire instr_sltu   = rvi_reg & rvi_func3_011 & rvi_func7_zero;
wire instr_csrrw  = rvi_sys & rvi_func3_001;
wire instr_csrrs  = rvi_sys & rvi_func3_010;
wire instr_csrrc  = rvi_sys & rvi_func3_011;
wire instr_csrrwi = rvi_sys & rvi_func3_101;
wire instr_csrrsi = rvi_sys & rvi_func3_110;
wire instr_csrrci = rvi_sys & rvi_func3_111;
wire instr_mret   = rvi_sys & bmu2idu_instr[31:7] == 25'h0604000;

wire branch_pc_set = instr_jal | instr_jalr | instr_mret | (instr_beq | instr_blt | instr_bltu) & alu2idu_cmp | (instr_bne | instr_bge | instr_bgeu) & ~alu2idu_cmp;

wire [17:0] branch_pc_new = instr_mret ? csr2idu_mepc : alu2idu_addro[19:2];

wire [31:0] gpr_wdata_lui = {32{instr_lui}} & {bmu2idu_instr[31:12], 12'd0};

wire [31:0] gpr_wdata_jal = {32{instr_jal | instr_jalr}} & {12'd0, bmu2idu_pc_nxt, 2'd0};

wire [31:0] gpr_wdata_alu = {32{rvi_aui | rvi_imm | rvi_reg}} & alu2idu_res;

wire [31:0] gpr_wdata_csr = {32{rvi_sys}} & csr2idu_rdata;

wire [31:0] gpr_wdata_lb0 = {32{instr_lb & ~idu2bmu_ls_addr[1] & ~idu2bmu_ls_addr[0]}} & {{24{bmu2idu_ls_rdata[7] & ~instr_lbu}}, bmu2idu_ls_rdata[7:0]};

wire [31:0] gpr_wdata_lb1 = {32{instr_lb & ~idu2bmu_ls_addr[1] &  idu2bmu_ls_addr[0]}} & {{24{bmu2idu_ls_rdata[15] & ~instr_lbu}}, bmu2idu_ls_rdata[15:8]};

wire [31:0] gpr_wdata_lb2 = {32{instr_lb &  idu2bmu_ls_addr[1] & ~idu2bmu_ls_addr[0]}} & {{24{bmu2idu_ls_rdata[23] & ~instr_lbu}}, bmu2idu_ls_rdata[23:16]};

wire [31:0] gpr_wdata_lb3 = {32{instr_lb &  idu2bmu_ls_addr[1] &  idu2bmu_ls_addr[0]}} & {{24{bmu2idu_ls_rdata[31] & ~instr_lbu}}, bmu2idu_ls_rdata[31:24]};

wire [31:0] gpr_wdata_lh0 = {32{instr_lh & ~idu2bmu_ls_addr[1]}} & {{16{bmu2idu_ls_rdata[15] & ~instr_lhu}}, bmu2idu_ls_rdata[15:0]};

wire [31:0] gpr_wdata_lh1 = {32{instr_lh &  idu2bmu_ls_addr[1]}} & {{16{bmu2idu_ls_rdata[31] & ~instr_lhu}}, bmu2idu_ls_rdata[31:16]};

wire [31:0] gpr_wdata_lw  = {32{instr_lw}} & bmu2idu_ls_rdata;

wire [31:0] csr_wdata_rw  = {32{instr_csrrw}} & gpr2idu_rdata1;

wire [31:0] csr_wdata_rs  = {32{instr_csrrs}} & (csr2idu_rdata | gpr2idu_rdata1);

wire [31:0] csr_wdata_rc  = {32{instr_csrrc}} & (csr2idu_rdata & ~gpr2idu_rdata1);

wire [31:0] csr_wdata_rwi = {32{instr_csrrwi}} & {27'd0, bmu2idu_instr[19:15]};

wire [31:0] csr_wdata_rsi = {32{instr_csrrsi}} & (csr2idu_rdata | {27'd0, bmu2idu_instr[19:15]});

wire [31:0] csr_wdata_rci = {32{instr_csrrci}} & (csr2idu_rdata & ~{27'd0, bmu2idu_instr[19:15]});

wire [3:0]  alu_op_beq  = {4{instr_beq  | instr_bne }} & 4'b1000;

wire [3:0]  alu_op_blt  = {4{instr_blt  | instr_bge }} & 4'b1010;

wire [3:0]  alu_op_bltu = {4{instr_bltu | instr_bgeu}} & 4'b1011;

wire [3:0]  alu_op_calc = {4{rvi_imm | rvi_reg}} & {bmu2idu_instr[30] & ~instr_addi | instr_slti | instr_sltiu | instr_slt | instr_sltu, bmu2idu_instr[14:12]};

wire [31:0] alu_rs2_aui = {32{instr_auipc}} & {12'd0, bmu2idu_pc_cur, 2'd0};

wire [31:0] alu_rs2_imm = {32{rvi_imm}} & {{20{bmu2idu_instr[31]}}, bmu2idu_instr[31:20]};

wire [31:0] alu_rs2_reg = {32{rvi_reg | rvi_bra}} & gpr2idu_rdata2;

wire [19:0] alu_addr1_jal = {20{instr_jal}} & {bmu2idu_instr[19:12], bmu2idu_instr[20], bmu2idu_instr[30:21], 1'b0};

wire [19:0] alu_addr1_bra = {20{rvi_bra}} & {{8{bmu2idu_instr[31]}}, bmu2idu_instr[7], bmu2idu_instr[30:25], bmu2idu_instr[11:8], 1'b0};

wire [19:0] alu_addr1_reg = {20{rvi_ldr | rvi_str | instr_jalr}} & gpr2idu_rdata1[19:0];

wire [19:0] alu_addr2_ldr = {20{instr_jalr | rvi_ldr}} & {{8{bmu2idu_instr[31]}}, bmu2idu_instr[31:20]};

wire [19:0] alu_addr2_str = {20{rvi_str}} & {{8{bmu2idu_instr[31]}}, bmu2idu_instr[31:25], bmu2idu_instr[11:7]};

wire [19:0] alu_addr2_bra = {20{instr_jal | rvi_bra}} & {bmu2idu_pc_cur, 2'd0};

assign idu2gpr_we = rvi_imm | rvi_reg | rvi_ldr & bmu2idu_pc_ack |  rvi_jal | rvi_jar | rvi_sys | rvi_lui | rvi_aui;

assign idu2gpr_waddr = bmu2idu_instr[10:7];

assign idu2gpr_raddr1 = bmu2idu_instr[18:15];

assign idu2gpr_raddr2 = bmu2idu_instr[23:20];

assign idu2gpr_wdata = gpr_wdata_lui | gpr_wdata_jal | gpr_wdata_alu | gpr_wdata_csr | gpr_wdata_lw  |
                       gpr_wdata_lb0 | gpr_wdata_lb1 | gpr_wdata_lb2 | gpr_wdata_lb3 | gpr_wdata_lh0 | gpr_wdata_lh1;

assign idu2csr_we = rvi_sys;

assign idu2csr_addr = bmu2idu_instr[31:20];

assign idu2csr_wdata = csr_wdata_rw | csr_wdata_rs | csr_wdata_rc | csr_wdata_rwi | csr_wdata_rsi | csr_wdata_rci;

assign idu2cic_int_ack = bmu2idu_pc_ack;

assign idu2csr_mepc_set = cic2idu_int_req & idu2cic_int_ack;

assign idu2bmu_pc_set = branch_pc_set | idu2csr_mepc_set;

assign idu2bmu_pc_new = idu2csr_mepc_set ? csr2idu_mtvec : branch_pc_new;

assign idu2csr_mepc_nxt = branch_pc_set ? branch_pc_new : bmu2idu_pc_nxt;

assign idu2cic_int_mret = instr_mret;

assign idu2alu_op = alu_op_beq | alu_op_blt | alu_op_bltu | alu_op_calc;

assign idu2alu_rs1 = {32{~instr_auipc}} & gpr2idu_rdata1 | {32{instr_auipc}} & {bmu2idu_instr[31:12], 12'd0};

assign idu2alu_rs2 = alu_rs2_aui | alu_rs2_imm | alu_rs2_reg;

assign idu2bmu_ls_req = rvi_ldr | rvi_str;

assign idu2bmu_ls_cmd = rvi_str;

assign idu2bmu_ls_size = bmu2idu_instr[13:12];

assign idu2bmu_ls_addr = alu2idu_addro;

assign idu2bmu_ls_wdata = {32{instr_sb}} & {4{gpr2idu_rdata2[7:0]}} | {32{instr_sh}} & {2{gpr2idu_rdata2[15:0]}} | {32{instr_sw}} & gpr2idu_rdata2;

assign idu2alu_addr1 = alu_addr1_jal | alu_addr1_bra | alu_addr1_reg;

assign idu2alu_addr2 = alu_addr2_ldr | alu_addr2_str | alu_addr2_bra;

endmodule
